﻿namespace Bowlus.FrontEnd
{
    using System.Collections.Generic;
    using System.Linq;
    using Bowlus.Common;
    using System;

    // This class contains the logic to place instances
    public class AppInstanceLocator
    {
        // TODO: 1) optimize rebalance logic, reduce moving tenanat arround,
        // 2) only one FrontEnd need to do rebalance in cases of topology change.
        //
        // In current design, even if multiple FrontEnds do rebalance at the same time, 
        // the system still works because the AppHost provision logic is very resilient.
        // AppHost always provision based on the last command issued by a FrontEnd, and
        // AppHost can handle errors like 'provision a site already provisioned'.
        
        // simple implementation: evenly distribute tenants on farms
        public static void Rebalance(IList<TenantDeployment> tds)
        {
            var appHosts = AppHostInstance.GetAppHosts();

            // Sort, to give consistant rebalance result.
            // Given the tds and farms, rebalance result should always be the same.
            var a_tds = tds.ToArray<TenantDeployment>();
            var a_ahs = appHosts.ToArray<AppHostInstance>();
            Array.Sort(a_tds);
            Array.Sort(a_ahs);

            for (int i = 0, j = 0; j < a_tds.Length; )
            {
                var newInstance = a_ahs[i].IP;
                var td = a_tds[j];
                if (td.Instance != newInstance)
                {
                    td.Status = TenantDeployment.S_MOVE;
                    td.Staging = newInstance;
                    td.StagingId = string.Empty;
                    td.SStatus = TenantDeployment.S_PROVISIONING;
                }

                i++;
                j++;
                if (i == a_ahs.Length)
                    i = 0;
            }
        }

        // return target instance IP
        public static string FindDeploymentSlot(IList<TenantDeployment> tds)
        {
            var appHosts = AppHostInstance.GetAppHosts();

            foreach (var ah in appHosts)
            {
                var count = (from td in tds
                           where td.Status == TenantDeployment.S_ACTIVE && td.Instance == ah.IP                       
                           select td).Count<TenantDeployment>();
                if (count == 0)
                    return ah.IP;
            }

            var instanceCounts = from td in tds
                                 where td.Status == TenantDeployment.S_ACTIVE
                                 group td by td.Instance
                                     into c
                                     select new { Instance = c.Key, Count = c.Count() };


            var smallestInstance = instanceCounts.OrderBy(ins => ins.Count).First();

            return smallestInstance.Instance;
        }
    }
}
